package by.dkusch.aircompany.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dkusch.aircompany.dao.PersonDao;
import by.dkusch.aircompany.model.filter.FilterPerson;
import by.dkusch.aircompany.model.filter.base.FilterEntity;
import by.dkusch.aircompany.model.tables.Crew;
import by.dkusch.aircompany.model.tables.Person;
import by.dkusch.aircompany.model.tables.Person_;

@Repository
public class PersonDaoImpl extends AbstractDaoImpl<Long, Person> implements PersonDao {

	public PersonDaoImpl() {
		super(Person.class);
	}

	@Override
	public Person getByIdWithLazyDetails(Long id) {

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		CriteriaQuery<Person> root = cBuilder.createQuery(Person.class);
		Root<Person> criteria = root.from(Person.class);

		root.select(criteria);
		root.where(cBuilder.equal(criteria.get(Person_.id), id));
		root.select(criteria);
		criteria.fetch(Person_.crewSet, JoinType.LEFT);
		root.distinct(true);

		TypedQuery<Person> query = getEm().createQuery(root);
		Person result = query.getSingleResult();
		return result;

	}

	@Override
	public Set<Crew> getCrewSet(Long id) {
		if (id != null) {
			Person person = getByIdWithLazyDetails(id);
			if (person != null) {
				return person.getCrewSet();
			}
		}
		return new TreeSet<Crew>();
	}

	@Override
	public List<Person> getAllItemsWithFilter(SingularAttribute<? super Person, ?> attr, boolean ascending, int startRecord, int pageSize, FilterEntity<Long, ? super Person> filter) {
		if (filter == null) {
			return getAllItems(attr, ascending, startRecord, pageSize);
		}

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Person> criteria = cBuilder.createQuery(Person.class);
		Root<Person> root = criteria.from(Person.class);

		criteria.select(root).distinct(true);

		if (attr != null) {
			criteria.orderBy(new OrderImpl(root.get(attr), ascending));
		}
		List<Predicate> conditions = new ArrayList<Predicate>();

		FilterPerson fc = (FilterPerson) filter;

		if (fc.getTabCode() != null) {
			conditions.add(cBuilder.equal(cBuilder.upper(root.get(Person_.tabCode)), fc.getTabCode().toUpperCase()));
		}

		if ((fc.getFirstName() != null) && (fc.getFIO() == null)) {
			conditions.add(cBuilder.like(cBuilder.upper(root.get(Person_.firstName)), fc.getFirstName().toUpperCase()));
		}

		if ((fc.getSecondName() != null) && (fc.getFIO() == null)) {
			conditions.add(cBuilder.like(cBuilder.upper(root.get(Person_.secondName)), fc.getSecondName().toUpperCase()));
		}

		if ((fc.getLastName() != null) && (fc.getFIO() == null)) {
			conditions.add(cBuilder.like(cBuilder.upper(root.get(Person_.lastName)), fc.getLastName().toUpperCase()));
		}

		if (fc.getFIO() != null) {
			conditions.add(cBuilder.like(root.get(Person_.uFIO), fc.getFIO().toUpperCase()));
		}
		criteria.where(cBuilder.and(conditions.toArray(new Predicate[] {})));

		TypedQuery<Person> query = getEm().createQuery(criteria);
		if (startRecord >= 0) {
			query.setFirstResult(startRecord);
			query.setMaxResults(pageSize);
		}
		List<Person> results = query.getResultList();
		return results;

	}

}
